﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Text;
using DataCenter.Interface;
using DataCenter.Interface.Model;
using DataCenter.Share;
using System.Diagnostics;

namespace DataCenter.Component.Manager
{
    public class ImportManager : IManager
    {
        public ImportManager(SqlCommand command, EntityCollection collection, bool isProcess)
        {
            this._Command = command;
            this._TableSetting = TableSetting.GetInstance();
            this._Collection = collection;
            this._IsProcess = isProcess;
        }
        private SqlCommand _Command;
        private TableSetting _TableSetting;
        private EntityCollection _Collection;
        private bool _IsProcess;
        private DateTime Time0;
        private DateTime Time1;
        
        private Dictionary<int, Dictionary<int, List<ObjectEntity>>> DictObjectGroup;       // 数据源编号->适配器编号->对象列表
        private Dictionary<int, Dictionary<int, TimeQueue>> DictTimeGroup;                  // 数据源编号->适配器编号->时间队列
        private Dictionary<string, List<ObjectEntity>> DictKey;                             // 对象Key->对象列表    
        private List<BinaryRecord> RecordList;

        #region IManager Members

        public bool Initialize(DateTime time0, DateTime time1)
        {
            this.Time0 = time0;
            this.Time1 = time1;            

            if (!InitObjectGroup()) return false;
            if (!InitObjectValue()) return false;

            foreach (int sourceId in DictTimeGroup.Keys)
            {
                foreach (int adapterId in DictTimeGroup[sourceId].Keys)
                {
                    TimeQueue queue = DictTimeGroup[sourceId][adapterId];
                    foreach (TimeRegion region in queue.RegionList)
                        Logger.GetInstance().Debug("\t导入时间段: " + region.ToString() + 
                                                   "; SourceId=" + sourceId + 
                                                   "; AdapterId=" + adapterId);
                }
            }

            return true;
        }

        public bool Execute()
        {
            AdapterValueCallbackDelegate callback = new AdapterValueCallbackDelegate(SaveValue);

            foreach (int sourceId in DictTimeGroup.Keys)
            {
                ComponentEntity sourceEntity = _Collection.GetComponent(sourceId);
                if (sourceEntity != null)
                {
                    using (ISource source = sourceEntity.CreateComponent<ISource>(_Command))
                    {
                        if (!source.Initialize())
                            continue;

                        foreach (int adapterId in DictTimeGroup[sourceId].Keys)
                        {
                            TimeQueue queue = DictTimeGroup[sourceId][adapterId];
                            foreach (TimeRegion region in queue.RegionList)
                            {
                                DictKey = new Dictionary<string, List<ObjectEntity>>();
                                List<string> keyList = new List<string>();
                                foreach (ObjectEntity objectEntity in region.DictObject.Values)
                                {
                                    string key = objectEntity.ObjectKey.Replace("'", "");

                                    List<ObjectEntity> entityList = null;
                                    if (DictKey.ContainsKey(key))
                                        entityList = DictKey[key];
                                    else
                                    {
                                        keyList.Add(objectEntity.ObjectKey);

                                        entityList = new List<ObjectEntity>();
                                        DictKey.Add(key, entityList);
                                    }
                                    entityList.Add(objectEntity);
                                }

                                ComponentEntity adapterEntity = _Collection.GetComponent(adapterId);
                                if (adapterEntity != null)
                                {
                                    using (IAdapter adapter = adapterEntity.CreateComponent<IAdapter>(_Command))
                                    {
                                        try
                                        {
                                            if (!adapter.Initialize())
                                                continue;

                                            Stopwatch watch1 = new Stopwatch();
                                            watch1.Start();
                                            if (!adapter.Execute(source, keyList.ToArray(), region.Time0, region.Time1))
                                                return false;
                                            watch1.Stop();
                                            Logger.GetInstance().Debug("\t数据读取时间：" + watch1.ElapsedMilliseconds);

                                            Stopwatch watch2 = new Stopwatch();
                                            watch2.Start();
                                            adapter.GetValue(callback);
                                            watch2.Stop();
                                            Logger.GetInstance().Debug("\t数据保存时间：" + watch2.ElapsedMilliseconds);
                                        }
                                        catch (Exception ex)
                                        {
                                            Logger.GetInstance().Error(string.Format("[ERROR]: 导入数据时发生错误。Source={0}; Adapter={1}",
                                                sourceEntity.ComponentName, adapterEntity.ComponentName), ex);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

            // 本次数据导入后，监测缓存的数据记录
            // 如果为历史记录，并且ImportTime任然为1900-1-1，
            // 则将ImportTime改为23:59，下次不再重复进行导入            
            UpdateImportTime();

            return true;
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
        }

        #endregion

        private bool UpdateImportTime()
        {
            foreach (BinaryRecord record in RecordList)
            {
                if (record.ImportTime == Utils.EMPTY_TIME)
                {
                    if (record.ValueTime != Utils.TODAY)
                    {
                        record.UpdateImportTime();
                        record.UpdateRecordTime(_Command);
                    }
                }
            }

            return true;
        }

        private bool SaveValue(string key, DateTime valueTime, TimeLine valueLine)
        {
            try
            {
                if (DictKey.ContainsKey(key))
                {
                    List<ObjectEntity> objectList = DictKey[key];
                    foreach (ObjectEntity objectEntity in objectList)
                    {
                        BinaryRecord record = objectEntity.GetRecord(valueTime);
                        Binary binary = null;
                        if (record == null)
                        {
                            binary = new DayBinary(valueLine);
                            binary.ValueTime = valueTime;

                            record = new BinaryRecord();
                            record.ObjectId = objectEntity.ObjectId;
                            record.ValueType = objectEntity.ObjectType;
                            record.ValueTime = valueTime;
                            record.BinaryValue = binary;

                            if (objectEntity.Unit != 1.0f)
                            {
                                binary.Calculate(Operators.Mul, 0, objectEntity.Unit, valueTime.Date, Utils.GetLastTime(valueTime));
                            }
                        }
                        else
                        {
                            if (Utils.IsLastTime(record.ImportTime)) continue;
                            if (Time1 < record.ImportTime) continue;

                            binary = record.BinaryValue;
                            binary.BeginEdit();
                            binary.SetValue(valueLine);
                            
                            if (objectEntity.Unit != 1.0f)
                            {
                                binary.Calculate(Operators.Mul, 0, objectEntity.Unit, valueLine.GetFirstTime(), valueLine.GetLastTime());
                            }
                        }
                        record.UpdateImportTime();

                        if (_IsProcess && objectEntity.CanProcess)
                        {
                            if (record.NewRecord)
                            {
                                objectEntity.AddRecord(record);
                            }
                            binary.EndEdit();
                            //record.IsSubmit = true;
                        }
                        else
                        {
                            if (record.NewRecord || record.ImportTime0 == Utils.EMPTY_TIME)
                            {
                                record.CreateRecord(_Command);
                            }
                            else
                            {
                                if (record.IsSubmit)
                                {
                                    record.UpdateRecord(_Command, record.ImportTime0, record.ImportTime);                                    
                                }
                                objectEntity.RemoveRecord(record.ValueTime);
                            }
                        }
                    }
                }

                return true;
            }
            catch (Exception ex)
            {
                return Logger.GetInstance().Error("[ERROR]: 保存导入数据时发生错误。", ex);
            }
        }

        private bool InitObjectGroup()
        {
            DictObjectGroup = new Dictionary<int, Dictionary<int, List<ObjectEntity>>>();
            foreach (ObjectEntity objectEntity in _Collection.DictObject.Values)
            {
                if (!objectEntity.IsVirtual)
                {
                    if (objectEntity.CanImport)
                    {
                        int sourceId = objectEntity.BindingSourceId;
                        int adapterId = objectEntity.BindingAdapterId;

                        Dictionary<int, List<ObjectEntity>> dictAdapter = null;
                        if (DictObjectGroup.ContainsKey(sourceId))
                            dictAdapter = DictObjectGroup[sourceId];
                        else
                        {
                            dictAdapter = new Dictionary<int, List<ObjectEntity>>();
                            DictObjectGroup.Add(sourceId, dictAdapter);
                        }

                        List<ObjectEntity> objectList = null;
                        if (dictAdapter.ContainsKey(adapterId))
                            objectList = dictAdapter[adapterId];
                        else
                        {
                            objectList = new List<ObjectEntity>();
                            dictAdapter.Add(adapterId, objectList);
                        }
                        objectList.Add(objectEntity);
                    }
                }
            }
            return true;
        }

        private bool InitObjectValue()
        {
            DictTimeGroup = new Dictionary<int, Dictionary<int, TimeQueue>>();
            RecordList = new List<BinaryRecord>();
            foreach (int sourceId in DictObjectGroup.Keys)
            {
                Dictionary<int, TimeQueue> dictQueue = new Dictionary<int, TimeQueue>();
                DictTimeGroup.Add(sourceId, dictQueue);
                foreach (int adapterId in DictObjectGroup[sourceId].Keys)
                {
                    List<ObjectEntity> objectList = DictObjectGroup[sourceId][adapterId];

                    TimeRegion region = new TimeRegion(Time0, Time1);
                    foreach (ObjectEntity objectEntity in objectList)
                        region.AddObject(objectEntity);
                    TimeQueue queue = new TimeQueue(region);
                    dictQueue.Add(adapterId, queue);

                    int sIndex = 0, eIndex = 0;
                    if (_TableSetting.AdjustTimeRegion(Time0, Time1, ref sIndex, ref eIndex))
                    {
                        string objectIds = _Collection.GetObjectIds(false);
                        string commandTemplet = @"SELECT ObjectId, ValueTime, ValueType, ImportTime, InterpolateTime, BinaryValue 
                                                FROM {0} WHERE ValueTime >= '" + Time0.Date + @"' AND ValueTime <= '" + Time1.Date + @"' AND ObjectId IN (" + objectIds + @")";

                        StringBuilder cmdStrBuilder = new StringBuilder();
                        for (int index = sIndex; index <= eIndex; index++)
                        {
                            string tableName = _TableSetting.GetTableName(index);

                            cmdStrBuilder.Append(" UNION ");
                            cmdStrBuilder.AppendFormat(commandTemplet, tableName);

                            if (cmdStrBuilder.Length > 7500 || index == eIndex)
                            {
                                string commandString = cmdStrBuilder.ToString();
                                commandString = commandString.Substring(7);

                                _Command.CommandText = commandString;
                                _Command.CommandType = CommandType.Text;
                                using (SqlDataReader reader = _Command.ExecuteReader(CommandBehavior.SequentialAccess))
                                {
                                    while (reader.Read())
                                    {
                                        BinaryRecord record = new BinaryRecord();
                                        record.Build(reader);

                                        ObjectEntity objectEntity = _Collection.GetObject(record.ObjectId);
                                        if (objectEntity != null)
                                        {
                                            if (Utils.IsLastTime(record.ImportTime))
                                            {
                                                queue.RemoveRegion(record.ImportTime.Date, objectEntity);
                                            }
                                            else
                                            {                                                
                                                queue.AddRegion(record.ImportTime, objectEntity);

                                                record.Build(reader, true);
                                                objectEntity.AddRecord(record);
                                            }
                                        }

                                        // 数据准备时，将历史记录中ImportTime为1900-1-1的记录缓存
                                        if (record.ImportTime == Utils.EMPTY_TIME)
                                        {
                                            if (record.ValueTime != Utils.TODAY)
                                            {
                                                RecordList.Add(record);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            return true;
        }
    }
}
